Algolia is fast by default. But network speed and bandwidth can vary. This page lists a few best practices you can implement to adapt to your users’ network conditions.

Prepare the connection to Algolia

When sending the first network request to a domain, a security handshake must happen, consisting of several round trips between the client and the Algolia server. If the handshake first happened when users typed their first keystroke, the speed of that first request would be significantly slower. Use a preconnect link to carry out the handshake immediately after loading the page but before any user interaction. To do this, add a link tag with your Algolia domain in the head of your page.
HTML
<link crossorigin href="https://YOUR_APPID-dsn.algolia.net" rel="preconnect" />

<!-- For example: -->
<link crossorigin href="https://B1G2GM9NG0-dsn.algolia.net" rel="preconnect" />

Add a loading indicator

Consider a user accessing your app in a subway:
  1. They type some characters
  2. Nothing happens
  3. They wait, but still, nothing happens
However, you can enhance the user experience by displaying a loading indicator to indicate something is happening. The indicator will display slightly after the last query has been sent to Algolia.To display a loading indicator in the search box, use the showLoadingIndicator option.Change the duration of the delay with stalledSearchDelay (on the instantSearch widget).For example:
JavaScript
const search = instantsearch({
indexName: 'instant_search',
stalledSearchDelay: 200, // this is the default value for the delay
searchClient,
})

search.addWidgets([
instantsearch.widgets.searchBox({
    container: '#searchBox',
    placeholder: 'Search for products',
    showLoadingIndicator: true, // this add the loading indicator
}),
])
All InstantSearch.js examples on this page assume you’ve included InstantSearch.js in your web page from a CDN. If, instead, you’re using it with a package manager, adjust how you import InstantSearch.js and its widgets.

Make your own loading indicator

You can also use the loading indicator with other widgets The following example shows how to make a custom component that writes Loading... when search stalls. If network conditions are optimal, users won’t see this message.
JavaScript
const search = instantsearch({
indexName: 'instant_search',
searchClient,
})

// The container to use
const loadingContainer = document.querySelector('#loading')

search.addWidgets([
{
    render({ searchMetadata = {} }) {
    const { isSearchStalled } = searchMetadata

    loadingContainer.innerHTML = isSearchStalled ? 'Loading...' : ''
    },
},
])

Turn off search-as-you-type

Algolia is designed to deliver a search-as-you-type experience. Yet, it can also lead to lag in slow network conditions because browsers can only run a limited number of parallel requests to the same domain. Reducing requests can help prevent further lag. Debouncing helps you limit requests and avoid processing non-necessary ones by only sending requests once users have stopped typing.
Implement debouncing at the searchBox level with the queryHook option. For example:
JavaScript
const search = instantsearch({
indexName: 'instant_search',
searchClient,
});

let timerId;
let timeout = 0;

search.addWidgets([
instantsearch.widgets.searchBox({
    container: document.querySelector('#searchBox'),
    placeholder: 'Search for products',
    queryHook(query, refine) {
    clearTimeout(timerId);
    timerId = setTimeout(() => refine(query), timeout);
    },
}),
]);
This function uses the option queryHook and is called on every keystroke. In the example, the code debounces the call to refine.When network speeds improve, you should restore search-as-you-type to offer users the best possible experience. You can use the Network Information API to detect connection changes.
The Network Information API is unavailable on some browsers.
JavaScript
const search = instantsearch({
indexName: 'instant_search',
searchClient,
});

let timerId;
let timeout = 0;

search.addWidgets([
instantsearch.widgets.searchBox({
    container: document.querySelector('#searchBox'),
    placeholder: 'Search for products',
    queryHook(query, refine) {
    clearTimeout(timerId);
    timerId = setTimeout(() => refine(query), timeout);
    },
}),
]);

const connection = navigator.connection;
if (connection) {
connection.addEventListener('change', function updateTimeout() {
    timeout = ['slow-2g', '2g'].includes(connection?.effectiveType) ? 400 : 0;
});
}

Select a debounce delay

The optimal debouncing delay should match your audience’s typing speed (typically 30 words per minute (WPM) on mobile devices and 40 WPM on desktop devices). If the delay is too short, users still see flashes of content. If the delay is too long, the time between keypresses and results becomes too long. 200 ms is the preferred debounce delay. Delays of over 300 ms will start degrading the user experience.

Optimize build size

InstantSearch supports dead code elimination through tree shaking, but you must follow a few rules for it to work:
  • Bundle your code using a module bundler that supports tree shaking with the sideEffects property in package.json, such as Rollup or webpack 4+.
  • Ensure you pick the ES module build of InstantSearch by targeting the module field in package.json (resolve.mainFields option in webpack, mainFields option in @rollup/plugin-node-resolve). This is the default configuration in most popular bundlers: you only need to change something if you have a custom configuration.
  • Keep Babel or other transpilers from transpiling ES6 modules to CommonJS modules. Tree shaking is much less optimal on CommonJS modules, so it’s better to let your bundler handle modules itself.
If you’re using Babel, you can configure babel-preset-env not to process ES6 modules:
JavaScript
// babel.config.js
module.exports = {
presets: [
    [
    'env',
    {
        modules: false,
    },
    ],
],
}

Troubleshooting

To check if tree shaking works, try to import InstantSearch into your project without using it.
JavaScript
import 'instantsearch.js/es' // Unused import
Build your app, then look for the unused code in your final bundle (for example, “InstantSearch”). If tree shaking works, you shouldn’t find anything.

Queries per second (QPS)

Search operations aren’t limited by a fixed “search quota”. Instead, they’re limited by your plan’s maximum QPS and operations limit. Every keystroke in InstantSearch using the search box counts as one operation. Then, depending on the widgets you add to your search interface, you may have more operations being counted on each keystroke. For example, you have a search interface with a search box, a hierarchical menu, and a refinement list. An operation is triggered for each:
  • Keystroke in the search box
  • User change to the hierarchical menu or refinement list`.
If you experience QPS limitations, consider implementing a debounced search box.